package com.tl.boot.common.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateUtil {
    private final static Logger log = LoggerFactory.getLogger(DateUtil.class);
    private final static int MIN_YEAR = 1970;

    private final static int MAX_YEAR = 9999;

    private final static int MIN_MONTH = 1;

    private final static int MAX_MONTH = 12;

    private final static int MIN_DAY_OF_MONTH = 1;

    private final static int MIN_HOUR = 0;

    private final static int MAX_HOUR = 23;

    private final static int MIN_MINUTE = 0;

    private final static int MAX_MINUTE = 59;

    private final static int MIN_SECOND = 0;

    private final static int MAX_SECOND = 59;

    private final static int MIN_MILLISECOND = 0;

    private final static int MAX_MILLISECOND = 999;

    public static int thisMonth() {
        return Integer.valueOf(timestampToString(System.currentTimeMillis(), PartFormatEnum.YM2).substring(4));
    }

    public static String today(FullFormatEnum formatEnum) {
        return timestampToString(System.currentTimeMillis(), formatEnum);
    }

    public static String today(PartFormatEnum formatEnum) {
        return timestampToString(System.currentTimeMillis(), formatEnum);
    }

    public static String formatDate(Date date, FullFormatEnum formatEnum) {
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(formatEnum.getFormat());
            return formatter.format(date.getTime());
        } catch (Exception e) {
            return null;
        }
    }

    public static String formatDate(Date date, PartFormatEnum formatEnum) {
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(formatEnum.getFormat());
            return formatter.format(date.getTime());
        } catch (Exception e) {
            return null;
        }
    }

    public static String timestampToString(Long time, FullFormatEnum formatEnum) {
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(formatEnum.getFormat());
            return formatter.format(time);
        } catch (Exception e) {
            return null;
        }
    }

    public static String timestampToString(Long time, PartFormatEnum formatEnum) {
        try {
            SimpleDateFormat formatter = new SimpleDateFormat(formatEnum.getFormat());
            return formatter.format(time);
        } catch (Exception e) {
            return null;
        }
    }

    public static Long stringToTimestamp(String dateStr, FullFormatEnum formatEnum) {
        if (StringUtils.isEmpty(dateStr)) {
            return null;
        }

        try {
            SimpleDateFormat formatter = new SimpleDateFormat(formatEnum.getFormat());
            return formatter.parse(dateStr).getTime();
        } catch (ParseException e) {
            return null;
        }
    }

    public static Long stringToTimestamp(String dateStr, PartFormatEnum formatEnum, CompletionEnum completionEnum) {
        if (StringUtils.isEmpty(dateStr) || dateStr.length() != formatEnum.getFormat().length()) {
            return null;
        }

        Integer year = getValueByDateType(dateStr, formatEnum, DateTypeEnum.YEAR);
        Integer month = getValueByDateType(dateStr, formatEnum, DateTypeEnum.MONTH);
        Integer dayOfMonth = getValueByDateType(dateStr, formatEnum, DateTypeEnum.DAY_OF_MONTH);
        Integer hour = getValueByDateType(dateStr, formatEnum, DateTypeEnum.HOUR);
        Integer minute = getValueByDateType(dateStr, formatEnum, DateTypeEnum.MINUTE);
        Integer second = getValueByDateType(dateStr, formatEnum, DateTypeEnum.SECOND);
        year = year != null ? year : (completionEnum == CompletionEnum.MIN ? MIN_YEAR : MAX_YEAR);
        month = month != null ? month : (completionEnum == CompletionEnum.MIN ? MIN_MONTH : MAX_MONTH);
        dayOfMonth = dayOfMonth != null ? dayOfMonth : (completionEnum == CompletionEnum.MIN ? MIN_DAY_OF_MONTH : getMonthLength(year, month));
        hour = hour != null ? hour : (completionEnum == CompletionEnum.MIN ? MIN_HOUR : MAX_HOUR);
        minute = minute != null ? minute : (completionEnum == CompletionEnum.MIN ? MIN_MINUTE : MAX_MINUTE);
        second = second != null ? second : (completionEnum == CompletionEnum.MIN ? MIN_SECOND : MAX_SECOND);

        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, second);
        calendar.set(Calendar.MILLISECOND, completionEnum == CompletionEnum.MIN ? MIN_MILLISECOND : MAX_MILLISECOND);
        return calendar.getTimeInMillis();
    }

    /**
     * 获取 某日期某时间段前/后的日期
     * @param timeTypeEnum interval*timeTypeEnum为最终参数
     * @param interval
     * @return 返回时间戳
     */
    public static Long calDate(TimeTypeEnum timeTypeEnum, Long interval) {
        return System.currentTimeMillis() + convertToMilliSecond(interval, timeTypeEnum);
    }

    /**
     * @param timeTypeEnum
     * @param interval
     * @param formatEnum
     * @return 返回formatEnum的日期格式
     */
    public static String calDate(TimeTypeEnum timeTypeEnum, Long interval, FullFormatEnum formatEnum) {
        return timestampToString(System.currentTimeMillis() + convertToMilliSecond(interval, timeTypeEnum), formatEnum);
    }

    public static String calDate(TimeTypeEnum timeTypeEnum, Long interval, PartFormatEnum formatEnum) {
        return timestampToString(System.currentTimeMillis() + convertToMilliSecond(interval, timeTypeEnum), formatEnum);
    }

    public static Long calDate(Long baseTimestamp, TimeTypeEnum timeTypeEnum, Long interval) {
        return baseTimestamp + convertToMilliSecond(interval, timeTypeEnum);
    }

    public static String calDate(Long baseTimestamp, TimeTypeEnum timeTypeEnum, Long interval, FullFormatEnum formatEnum) {
        return timestampToString(baseTimestamp + convertToMilliSecond(interval, timeTypeEnum), formatEnum);
    }

    public static String calDate(Long baseTimestamp, TimeTypeEnum timeTypeEnum, Long interval, PartFormatEnum formatEnum) {
        return timestampToString(baseTimestamp + convertToMilliSecond(interval, timeTypeEnum), formatEnum);
    }

    /**
     * 获取某年某月份的天数
     * @param year 年份
     * @param month 月份
     * @return Integer year年month月的天数
     */
    public static Integer getMonthLength(Integer year, Integer month) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, month - 1);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    public static int subDays(Long timestamp1, Long timestamp2, DaysDegreeEnum daysDegreeEnum) {
        double days = 1.0D * (timestamp1 - timestamp2) / (24L * 60 * 60 * 1000);

        switch (daysDegreeEnum) {
            case CEIL:
                return (int)Math.ceil(days);
            case FLOOR:
            default:
                return (int)Math.floor(days);
        }
    }

    public static int subDays(String dateStr1, String dateStr2, PartFormatEnum formatEnum) {
        return subDays(stringToTimestamp(dateStr1, formatEnum, CompletionEnum.MIN), stringToTimestamp(dateStr2, formatEnum, CompletionEnum.MIN), DaysDegreeEnum.FLOOR);
    }

    public enum FullFormatEnum {
        YMDHMS1("yyyy-MM-dd HH:mm:ss"),
        YMDHMS2("yyyyMMddHHmmss");

        private String format;

        FullFormatEnum(String format) {
            this.format = format;
        }

        public String getFormat() {
            return format;
        }
    }

    public enum PartFormatEnum {
        YMD1("yyyy-MM-dd"),
        YMD2("yyyyMMdd"),
        YM1("yyyy-MM"),
        YM2("yyyyMM");

        private String format;

        PartFormatEnum(String format) {
            this.format = format;
        }

        public String getFormat() {
            return format;
        }
    }

    public enum TimeTypeEnum {
        MILLISECOND,
        SECOND,
        MINUTE,
        HOUR,
        DAY
    }

    public enum CompletionEnum {
        MIN,
        MAX
    }

    public enum DaysDegreeEnum {
        FLOOR,
        CEIL
    }

    private enum DateTypeEnum {
        YEAR,
        MONTH,
        DAY_OF_MONTH,
        HOUR,
        MINUTE,
        SECOND
    }

    private static Long convertToMilliSecond(Long interval, TimeTypeEnum timeTypeEnum) {
        switch (timeTypeEnum) {
            case MILLISECOND:
                return interval;
            case SECOND:
                return interval * 1000;
            case MINUTE:
                return interval * 60 * 1000;
            case HOUR:
                return interval * 60 * 60 * 1000;
            case DAY:
                return interval * 24 * 60 * 60 * 1000;
            default:
                return 0L;
        }
    }

    private static Integer getValueByDateType(String dateStr, PartFormatEnum formatEnum, DateTypeEnum dateTypeEnum) {
        switch (dateTypeEnum) {
            case YEAR:
                return Integer.valueOf(dateStr.substring(0, 4));
            case MONTH:
                switch (formatEnum) {
                    case YMD1:
                    case YM1:
                        return Integer.valueOf(dateStr.substring(5, 7));
                    default:
                        return Integer.valueOf(dateStr.substring(4, 6));
                }
            case DAY_OF_MONTH:
                switch (formatEnum) {
                    case YMD1:
                        return Integer.valueOf(dateStr.substring(8, 10));
                    case YMD2:
                        return Integer.valueOf(dateStr.substring(6, 8));
                    default:
                        return null;
                }
            case HOUR:
            case MINUTE:
            case SECOND:
                return null;
            default:
                return null;
        }
    }
}